using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Resources;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Web.Caching;
using PostSharp.Extensibility;
using PostSharp.Laos;

namespace CommonLibrary.Caching
{
	#region Enums

	public enum CacheDurationType
	{
		Second = 1000,
		Minute = 60000,
		Hour = 3600000
	}

	public enum CacheMethodType
	{
		/// <summary>
		/// The item will be cached the first time it's requested
		/// </summary>
		OnRequest,

		/// <summary>
		/// The item will be cached at application start and persisted in cache
		/// </summary>
		PreLoad
	}

	#endregion

	#region Attributes

	[Serializable]
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly,Inherited = true,AllowMultiple = true)]
	[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true, AllowMultiple = true)]
	public class ClearCacheAttribute : OnMethodBoundaryAspect
	{
		#region Private Fields

		private static MessageSource MessageSource =
			new MessageSource("CommonLibrary.Properties",
							  new ResourceManager("CommonLibrary.Properties.Resources", Assembly.GetExecutingAssembly()));

		private string _associatedMethodName;
		private int[] _parameterValues;

		#endregion

		#region Public Properties

		/// <summary>
		/// The name of the method that creates the cache item this clears
		/// </summary>
		public string AssociatedMethodName
		{
			get { return _associatedMethodName; }
			set { _associatedMethodName = value; }
		}

		/// <summary>
		/// The parameters that build the key for the cache
		/// </summary>
		public int[] ParameterValues
		{
			get { return _parameterValues; }
			set { _parameterValues = value; }
		}

		#endregion

		#region Public Methods

		public override void OnSuccess(MethodExecutionEventArgs eventArgs)
		{
			List<object> associatedParamList = new List<object>();
			if (ParameterValues != null)
			{
				object[] paramList = eventArgs.GetArguments();
				foreach (int i in ParameterValues)
				{
					associatedParamList.Add(paramList[i]);
				}
			}

			string key =
				Caching.CreateKeyFromMethod(eventArgs.Instance.GetType(), AssociatedMethodName + "()", associatedParamList.ToArray()); 
		
			Caching.RemoveFromCache(key);
		}

		#endregion
	}

	[Serializable]
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly,Inherited = true,AllowMultiple = true)]
	[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true, AllowMultiple = true)]
	public class UseCacheAttribute : OnMethodBoundaryAspect
	{
		#region Private Fields

		private static MessageSource MessageSource =
			new MessageSource("CommonLibrary.Properties",
			                  new ResourceManager("CommonLibrary.Properties.Resources", Assembly.GetExecutingAssembly()));

		private int? _duration;
		private string _cacheGroup;
		private CacheMethodType _cacheType = CacheMethodType.OnRequest;
		private string _noCachePattern;

		#endregion

		#region Public Properties

		/// <summary>
		/// The way to cache this item
		/// </summary>
		public CacheMethodType CacheMethod
		{
			get { return _cacheType; }
			set { _cacheType = value; }
		}

		/// <summary>
		/// The group to associate this cache item with, defaults to null
		/// </summary>
		public string CacheGroup
		{
			get { return _cacheGroup; }
			set { _cacheGroup = value; }
		}

		/// <summary>
		/// The length of time (in hours) to retain the item in cache.
		/// Default is 24
		/// </summary>
		public int Duration
		{
			get { return _duration.GetValueOrDefault(Caching.DefaultDuration); }
			set { _duration = value; }
		}

		/// <summary>
		/// The pattern to check against for testing whether the cache should be used
		/// </summary>
		public string NoCachePattern
		{
			get { return _noCachePattern ?? Caching.DefaultNoCachePattern; }
			set { _noCachePattern = value; }
		}

		#endregion		

		#region Public Methods

		public override void OnEntry(MethodExecutionEventArgs eventArgs)
		{
			string key = Caching.CreateKeyFromMethod(eventArgs.Method, eventArgs.GetArguments());

			if (Caching.ContainsKey(key))
			{
				eventArgs.ReturnValue = Caching.GetFromCache(key);
				eventArgs.FlowBehavior = FlowBehavior.Return;
			}
			else
			{
				eventArgs.MethodExecutionTag = key;
			}
		}

		public override bool CompileTimeValidate(MethodBase method)
		{
			bool isValid = true;
			string messageNumber = string.Empty;

			//Caching is meaningless on methods that have no return value
			if (method.IsConstructor || ((MethodInfo) method).ReturnType == typeof (void))
			{
				isValid = false;
				messageNumber = "Caching_Error0001";
			}
			else if (CacheMethod == CacheMethodType.PreLoad)
			{
				//Pre-loaded methods cannot require parameters
				if (method.GetParameters().Length > 0)
				{
					isValid = false;
					messageNumber = "Caching_Error0003";
				}
				else if (!method.IsStatic)
				{
					//TODO: *** Find out why this throws a method not implemented exception on build ***
					//
					//If the method isn't static, it's declaring type must have a constructor
					//with no parameters so that it can be instantiated 
					//ConstructorInfo con = method.DeclaringType.GetConstructor(Type.EmptyTypes);
					//if (con == null)
					//{
					//    isValid = false;
					//    messageNumber = "Caching_Error0004";
					//}
				}
			}
			else
			{
				//Can't work on methods with ref / out parameters
				ParameterInfo[] parameters = method.GetParameters();
				foreach (ParameterInfo param in parameters)
				{
				    if (param.IsOut)
				    {
				        isValid = false;
				        messageNumber = "Caching_Error0002";
				        break;
				    }
				}
			}

			if (!isValid)
			{
				MessageSource.Write(SeverityType.Error, messageNumber, null);
			}

			return isValid;
		}

		/// <summary>
		/// Called when the method has executed
		/// </summary>
		/// <param name="eventArgs"></param>
		public override void OnSuccess(MethodExecutionEventArgs eventArgs)
		{
			string key = eventArgs.MethodExecutionTag.ToString();

			Caching.AddToCache(CacheGroup, key, eventArgs.ReturnValue, Duration);
		}

		#endregion
	}

	#endregion

	#region Configuration Handlers

	/// <summary>
	/// Config handler to load the settings for the caching
	/// </summary>
	public class CacheConfigurationHandler : ConfigurationSection
	{
		public static CacheConfigurationHandler Load()
		{
			return (CacheConfigurationHandler)ConfigurationManager.GetSection("cache");
		}

		/// <summary>
		/// The assemblies to scan for pre-cache methods
		/// </summary>
		[ConfigurationProperty("assemblies")]
		public CacheAssemblies Assemblies
		{
			get { return (CacheAssemblies)base["assemblies"]; }
			set { base["assemblies"] = value; }
		}

		/// <summary>
		/// Default duration amount
		/// </summary>
		[ConfigurationProperty("defaultDuration", DefaultValue = 24, IsRequired = false)]
		public int DefaultDuration
		{
			get { return (int)base["defaultDuration"]; }
			set { base["defaultDuration"] = value; }
		}

		/// <summary>
		/// Default duration amount
		/// </summary>
		[ConfigurationProperty("defaultNoCachePattern", DefaultValue = null, IsRequired = false)]
		public string DefaultNoCachePattern
		{
			get { return base["defaultNoCachePattern"].ToString(); }
			set { base["defaultNoCachePattern"] = value; }
		}

		/// <summary>
		/// What each unit of duration actually means
		/// </summary>
		[ConfigurationProperty("durationType", DefaultValue = CacheDurationType.Hour, IsRequired = false)]
		public CacheDurationType DurationType
		{
			get { return (CacheDurationType)base["durationType"]; }
			set { base["durationType"] = value.ToString(); }
		}

	}

	public class CacheAssemblies : ConfigurationElementCollection
	{
		protected override ConfigurationElement CreateNewElement()
		{
			return new CacheAssembly();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			return ((CacheAssembly)element).AssemblyName;
		}
	}

	public class CacheAssembly : ConfigurationElement
	{
		#region Private Fields

		private const string ASSEMBLY_KEY = "assemblyName";
		private Assembly _assemblyReference;

		#endregion

		/// <summary>
		/// The name of the assembly to load
		/// </summary>
		[ConfigurationProperty(ASSEMBLY_KEY, IsRequired = true)]
		public string AssemblyName
		{
			get { return base[ASSEMBLY_KEY].ToString(); }
			set { base[ASSEMBLY_KEY] = value; }
		}

		/// <summary>
		/// The actual assembly that <see cref="AssemblyName"/> refers to
		/// </summary>
		public Assembly AssemblyReference
		{
			get
			{
				if (_assemblyReference == null)
				{
					try
					{
						_assemblyReference = Assembly.Load(AssemblyName);
					}
					catch (Exception ex)
					{
						throw new Exception(string.Format("Cannot load assembly '{0}' for pre load caching", AssemblyName), ex);
					}
				}
				return _assemblyReference;
			}
		}
	}

	#endregion

	#region Classes

	/// <summary>
	/// Factory class to generate a cache item for the preload cache monitor process. See Caching.ProcessList()
	/// </summary>
	internal class CacheProcessor
	{
		#region Private Fields

		private object _declaringObject;
		private MethodInfo _method;
		private Type _declaringType;
		private int _duration;
		private DateTime _nextRunTime;

		#endregion

		#region Constructor

		public CacheProcessor(Type declaringType, MethodInfo method, int duration)
		{
			DeclaringType = declaringType;
			Method = method;
			if (!Method.IsStatic)
			{
				try
				{
					DeclaringObject = DeclaringType.GetConstructor(Type.EmptyTypes).Invoke(null);
				}
				catch (Exception ex)
				{
					throw new Exception(
						string.Format("Unable to instantiate declaring type {0} for method {1}", DeclaringType, Method.Name), ex);
				}
			}
			Duration = duration;
			NextRunTime = DateTime.Now;
		}

		#endregion

		#region Public Methods

		public object GenerateCacheItem()
		{
			return Method.Invoke(DeclaringObject, (Method.IsStatic ? BindingFlags.Static : BindingFlags.Instance) |
			                                      BindingFlags.Public |
			                                      BindingFlags.InvokeMethod, null, null, null);
		}

		/// <summary>
		/// Generate the cache item & add it to cache
		/// </summary>
		public void Process()
		{
			Caching.AddToCache(Caching.CreateKeyFromMethod(Method), GenerateCacheItem(), Duration);
			NextRunTime = DateTime.Now.Add(Caching.GenerateDuration(Duration));
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// The number of hours to cache the object for
		/// </summary>
		public int Duration
		{
			get { return _duration; }
			set { _duration = value; }
		}

		/// <summary>
		/// The type of the object that has the cache method
		/// </summary>
		public Type DeclaringType
		{
			get { return _declaringType; }
			private set { _declaringType = value; }
		}

		/// <summary>
		/// The method that returns the cache object
		/// </summary>
		public MethodInfo Method
		{
			get { return _method; }
			private set { _method = value; }
		}

		/// <summary>
		/// The object that has the cache method, will be null for static methods
		/// </summary>
		public object DeclaringObject
		{
			get { return _declaringObject; }
			private set { _declaringObject = value; }
		}

		/// <summary>
		/// The next time the item should be regenerated
		/// </summary>
		public DateTime NextRunTime
		{
			get { return _nextRunTime; }
			set { _nextRunTime = value; }
		}

		#endregion
	}

	/// <summary>
	/// Static helper class for processing the cache
	/// </summary>
	public static class Caching
	{
		#region Private Fields
		
		public const string DEFAULT_GROUP_NAME = "general";

		private static Cache _cacheObject;
		private static HttpContext _context;

		private static List<CacheProcessor> _processorList;
		private static bool _stopRequested;
		private static TimeSpan _singleDurationUnit;
		private static int _defaultDuration;
		private static string _defaultNoCachePattern;

		#endregion

		#region Public Properties

		/// <summary>
		/// Default pattern for excluding cache calls
		/// </summary>
		public static string DefaultNoCachePattern
		{
			get { return _defaultNoCachePattern; }
			private set { _defaultNoCachePattern = value; }
		}

		/// <summary>
		/// The default duration amount
		/// </summary>
		public static int DefaultDuration
		{
			get { return _defaultDuration; }
			private set { _defaultDuration = value; }
		}

		/// <summary>
		/// A timespan representing a single unit of duration
		/// </summary>
		public static TimeSpan SingleDurationUnit
		{
			get { return _singleDurationUnit; }
			private set { _singleDurationUnit = value; }
		}

		#endregion

		#region Private Properties

		/// <summary>
		/// The HttpContext to check patterns against
		/// </summary>
		private static HttpContext Context
		{
			get { return _context; }
			set { _context = value; }
		}

		/// <summary>
		/// The cache, passed in
		/// </summary>
		private static Cache CacheObject
		{
			get { return _cacheObject; }
			set { _cacheObject = value; }
		}

		/// <summary>
		/// If stopping has been called
		/// </summary>
		private static bool StopRequested
		{
			get { return _stopRequested; }
			set { _stopRequested = value; }
		}

		/// <summary>
		/// The list of cache processors
		/// </summary>
		private static List<CacheProcessor> ProcessorList
		{
			get { return _processorList; }
			set { _processorList = value; }
		}	

		#endregion

		#region Public Methods

		/// <summary>
		/// Stop the preload cache monitor process
		/// </summary>
		public static void Stop()
		{
			StopRequested = true;
		}

		/// <summary>
		/// Generate a key from a type and a method name
		/// </summary>
		/// <param name="declaringType"></param>
		/// <param name="methodName"></param>
		/// <param name="parameterValues"></param>
		/// <returns></returns>
		public static string CreateKeyFromMethod(Type declaringType, string methodName, object[] parameterValues)
		{
			string key = string.Format("{0}.{1}", declaringType, methodName);
			if (parameterValues != null && parameterValues.Length == 0)
			{
				key = string.Format("{0} {1}", key, StringTools.Join("_", parameterValues));
			}
			return key.Trim();
		}

		/// <summary>
		/// Generate a key based on the method passed in
		/// </summary>
		/// <param name="method"></param>
		/// <returns></returns>
		public static string CreateKeyFromMethod(MethodBase method)
		{
			return method.ToString().Replace(" ", ".").Trim();
		}

		/// <summary>
		/// Generate a key based on the key & parameters
		/// </summary>
		/// <param name="method"></param>
		/// <param name="parameterValues"></param>
		/// <returns></returns>
		public static string CreateKeyFromMethod(MethodBase method, object[] parameterValues)
		{
			string key = CreateKeyFromMethod(method);
			if (parameterValues != null && parameterValues.Length > 0)
			{
				key = string.Format("{0} {1}", key, StringTools.Join("_", parameterValues));
			}
			return key.Trim();
		}
		
		/// <summary>
		/// Add an item to cache
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="duration"></param>
		public static void AddToCache(string key, object value, int duration)
		{
			AddToCache(null, key, value, duration);
		}

		/// <summary>
		/// Add an item to cache that is associated with a group.
		/// </summary>
		/// <param name="group"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="duration"></param>
		public static void AddToCache(string group, string key, object value, int duration)
		{
			AddToCache(group, key, value, duration, CacheItemPriority.Default, null);
		}

		/// <summary>
		/// Add an item to cache that is associated with a group.  Gives access to all cache parameters
		/// </summary>
		/// <param name="group"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="duration"></param>
		/// <param name="priority"></param>
		/// <param name="callback"></param>
		public static void AddToCache(string group, string key, object value, int duration, CacheItemPriority priority, CacheItemRemovedCallback callback)
		{
			CacheDependency dep = null;
			if (!string.IsNullOrEmpty(group))
			{
				CreateGroup(group);
				dep = new CacheDependency(null, new string[] { group });
			}

			CacheObject.Insert(key, value, dep, Cache.NoAbsoluteExpiration, GenerateDuration(duration), priority, callback);			
		}

		/// <summary>
		/// Remove the item from cache that matches the key
		/// </summary>
		/// <param name="key"></param>
		public static void RemoveFromCache(string key)
		{
			CacheObject.Remove(key);
		}

		/// <summary>
		/// Creates a cache group
		/// </summary>
		/// <param name="groupName"></param>
		public static void CreateGroup(string groupName)
		{
			CacheObject.Insert(groupName, DateTime.Now, null, Cache.NoAbsoluteExpiration, new TimeSpan(365, 0, 0, 0, 0), CacheItemPriority.NotRemovable, delegate(string key, object value, CacheItemRemovedReason reason) { CreateGroup(key); });
		}

		/// <summary>
		/// Refreshes a group object in cache, which clears all dependant items
		/// </summary>
		/// <param name="groupName"></param>
		public static void ClearGroup(string groupName)
		{
			if (ContainsKey(groupName))
			{
				CacheObject[groupName] = DateTime.Now;
			}
			else
			{
				CreateGroup(groupName);
			}
		}

		/// <summary>
		/// Generate a timespan that represents the duration based on the number passed in and the duration
		/// type specified in config
		/// </summary>
		/// <param name="duration"></param>
		/// <returns></returns>
		public static TimeSpan GenerateDuration(int duration)
		{
			double miliseconds = SingleDurationUnit.TotalMilliseconds * duration;
			return new TimeSpan(0, 0, 0, 0, (int)miliseconds);
		}

		/// <summary>
		/// Return an object from the cache based on the key
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public static object GetFromCache(string key)
		{
			return GetFromCache<object>(key);
		}

		/// <summary>
		/// Return a strongly typed object based on the key
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="key"></param>
		/// <returns></returns>
		public static T GetFromCache<T>(string key)
		{
			return (T) CacheObject[key];
		}

		/// <summary>
		/// Check whether an item exists in cache
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public static bool ContainsKey(string key)
		{
			try
			{
				return (CacheObject.Get(key) != null);
			}
			catch (NullReferenceException ex)
			{
				throw new NullReferenceException("Cache has not been initialised yet", ex);
			}
		}

		/// <summary>
		/// Match the pattern passed in against the current path
		/// </summary>
		/// <param name="pattern"></param>
		/// <returns></returns>
		public static bool MatchPattern(string pattern)
		{
			if (HttpContext.Current != null)
			{
				return Regex.Match(HttpContext.Current.Request.RawUrl, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase | RegexOptions.Compiled).Success;	
			}
			else
			{
				return false;
			}
			
		}

		/// <summary>
		/// Setup the cache and initialise the cache objects
		/// </summary>
		/// <param name="context"></param>
		public static void InitialiseCache(HttpContext context)
		{
			Context = context;
			CacheObject = Context.Cache;

			CacheConfigurationHandler config = CacheConfigurationHandler.Load();
			SingleDurationUnit = new TimeSpan(0,0,0,0,(int)config.DurationType);

			DefaultDuration = config.DefaultDuration;
			DefaultNoCachePattern = config.DefaultNoCachePattern;

			if (config.Assemblies.Count > 0)
			{
				ProcessorList = new List<CacheProcessor>();
				foreach (CacheAssembly cacheAssembly in config.Assemblies)
				{
					foreach (Type type in cacheAssembly.AssemblyReference.GetTypes())
					{
						foreach (MethodInfo info in type.GetMethods())
						{
							UseCacheAttribute[] ats = (UseCacheAttribute[]) info.GetCustomAttributes(typeof (UseCacheAttribute), true);
							if (ats.Length > 0)
							{
								//Loop through each UseCacheAttribute on the method until one with a 
								//CacheMethod of PreLoad is found then go to the next method.
								
								//HACK: This shouldn't be needed, but you can apply multiple UseCacheAttributes to a single method, though you really shouldn't
								foreach (UseCacheAttribute cacheAttribute in ats)
								{
									if (cacheAttribute.CacheMethod == CacheMethodType.PreLoad)
									{
										ProcessorList.Add(new CacheProcessor(type, info, cacheAttribute.Duration));
										break;
									}
								}
							}
						}
					}
				}

				if (ProcessorList.Count > 0)
				{
					Thread th = new Thread(delegate() { ProcessList(); });
					th.Start();					
				}
			}
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Monitor process that runs in a separate thread
		/// </summary>
		private static void ProcessList()
		{
			while(!StopRequested)
			{
				foreach (CacheProcessor processor in ProcessorList)
				{
					if (processor.NextRunTime <= DateTime.Now)
					{
						processor.Process();
					}
				}

				Thread.Sleep(SingleDurationUnit);
			}
		}

		#endregion
	}

	#endregion
}

#region Executor Class ***DEPRECATED***

//#region Config Handler

//public class CacheProviderConfigurationHandler : ConfigurationSection
//{
//    [ConfigurationProperty("providers")]
//    public CacheProviderSettingsCollection Providers
//    {
//        get { return (CacheProviderSettingsCollection)base["providers"]; }
//    }
//}

//public class CacheProviderSettingsCollection : ConfigurationElementCollection
//{
//    public const string KEY_NAME = "key";
//    public const string DURATION_NAME = "duration";
//    public const string ASSEMBLY_NAME = "assemblyName";
//    public const string METHOD_NAME = "methodName";
//    public const string CLASS_NAME = "className";
//    public const string DEPENDANCY_NAME = "dependancy";
//    public const string DEPENDANTPROPERTY_NAME = "dependantProperty";

//    protected override ConfigurationElement CreateNewElement()
//    {
//        return new CacheProviderSettings();
//    }

//    protected override object GetElementKey(ConfigurationElement element)
//    {
//        return ((CacheProviderSettings)element).Key;
//    }

//    #region CacheProviderSettings Object

//    public class CacheProviderSettings : ConfigurationElement
//    {
//        [ConfigurationProperty(KEY_NAME, IsRequired = true, IsKey = true)]
//        public string Key
//        {
//            get { return base[KEY_NAME].ToString(); }
//            set { base[KEY_NAME] = value; }
//        }

//        [ConfigurationProperty(DURATION_NAME, IsRequired = true)]
//        public int Duration
//        {
//            get { return Convert.ToInt32(base[DURATION_NAME]); }
//            set { base[DURATION_NAME] = value; }
//        }

//        [ConfigurationProperty(ASSEMBLY_NAME, IsRequired = true)]
//        public string AssemblyName
//        {
//            get { return base[ASSEMBLY_NAME].ToString(); }
//            set { base[ASSEMBLY_NAME] = value; }
//        }

//        [ConfigurationProperty(METHOD_NAME, IsRequired = true)]
//        public string MethodName
//        {
//            get { return base[METHOD_NAME].ToString(); }
//            set { base[METHOD_NAME] = value; }
//        }

//        [ConfigurationProperty(CLASS_NAME, IsRequired = true)]
//        public string ClassName
//        {
//            get { return base[CLASS_NAME].ToString(); }
//            set { base[CLASS_NAME] = value; }
//        }

//        [ConfigurationProperty(DEPENDANCY_NAME, IsRequired = false, DefaultValue = null)]
//        public string Dependancy
//        {
//            get { return base[DEPENDANCY_NAME].ToString(); }
//            set { base[DEPENDANCY_NAME] = value; }
//        }

//        [ConfigurationProperty(DEPENDANTPROPERTY_NAME, IsRequired = false, DefaultValue = null)]
//        public string DependantProperty
//        {
//            get { return base[DEPENDANTPROPERTY_NAME].ToString(); }
//            set { base[DEPENDANTPROPERTY_NAME] = value; }
//        }
//    }

//    #endregion
//}

//#endregion

////CacheProviderConfigurationHandler config = (CacheProviderConfigurationHandler)ConfigurationManager.GetSection("cacheProvider");

////if (config != null && cacheObject != null)
////{
////    CacheObject = cacheObject;

////    ExecutorList settingsList = new ExecutorList(config.Providers);
////    settingsList.Execute();

////    foreach (Executor exec in settingsList.Values)
////    {
////        foreach (KeyValuePair<string, object> pair in exec.ConstructedObjects)
////        {
////            AddToCache(pair.Key, pair.Value, exec.Settings.Duration);
////        }
////    }
////}
//private class Executor
//{
//    #region Constructors

//    public Executor(CacheProviderSettingsCollection.CacheProviderSettings settings)
//    {
//        Settings = settings;
//        ControllerObject = Activator.CreateInstance(Settings.AssemblyName, Settings.ClassName).Unwrap();
//    }

//    #endregion

//    #region Private Fields

//    private object _controllerObject;
//    private CacheProviderSettingsCollection.CacheProviderSettings _settings;
//    private Executor _dependantExecutor;
//    private Dictionary<string, object> _constructedObjects;

//    #endregion

//    #region Public Properties

//    /// <summary>
//    /// The list of cache items that are constructed by this Executor
//    /// </summary>
//    public Dictionary<string, object> ConstructedObjects
//    {
//        get { return _constructedObjects; }
//        set { _constructedObjects = value; }
//    }

//    /// <summary>
//    /// The executor this executor depends on
//    /// </summary>
//    public Executor DependantExecutor
//    {
//        get { return _dependantExecutor; }
//        set { _dependantExecutor = value; }
//    }

//    /// <summary>
//    /// The object that returns the cache object
//    /// </summary>
//    public object ControllerObject
//    {
//        get { return _controllerObject; }
//        private set { _controllerObject = value; }
//    }		

//    public CacheProviderSettingsCollection.CacheProviderSettings Settings
//    {
//        get { return _settings; }
//        private set { _settings = value; }
//    }

//    #endregion

//    #region Public Methods

//    /// <summary>
//    /// Generate the ConstructedObject
//    /// </summary>
//    public void Execute()
//    {
//        ConstructedObjects = new Dictionary<string, object>();

//        //If there's a dependancy, process it first
//        if (DependantExecutor != null)
//        {
//            DependantExecutor.Execute();
//            foreach (KeyValuePair<string, object> valuePair in DependantExecutor.ConstructedObjects)
//            {
//                object constructedObject = valuePair.Value;

//                if (constructedObject != null)
//                {
//                    if (constructedObject is ICollection)
//                    {
//                        foreach (object childObject in (ICollection)constructedObject)
//                        {
//                            object parameter = GetDependantPropertyValue(childObject, Settings.DependantProperty);
//                            string key = string.Format("{0}_{1}_{2}", Settings.Key, Settings.DependantProperty, parameter);

//                            ConstructedObjects.Add(key, ConstructCacheItem(parameter));
//                        }
//                    }
//                    else
//                    {
//                        object parameter = GetDependantPropertyValue(constructedObject, Settings.DependantProperty);
//                        string key = string.Format("{0}_{1}_{2}", Settings.Key, Settings.DependantProperty, parameter);

//                        ConstructedObjects.Add(key, ConstructCacheItem(parameter));
//                    }
//                }
//            }
//        }
//        else
//        {
//            ConstructedObjects.Add(Settings.Key, ConstructCacheItem());
//        }
//    }

//    /// <summary>
//    /// Gets the value of the property 'propertyName' on object 'obj'
//    /// </summary>
//    /// <param name="obj"></param>
//    /// <param name="propertyName"></param>
//    /// <returns></returns>
//    private object GetDependantPropertyValue(object obj, string propertyName)
//    {
//        try
//        {
//            return obj.GetType().InvokeMember(propertyName, BindingFlags.Public |
//                                                    BindingFlags.GetProperty |
//                                                    BindingFlags.Instance,
//                                        null, obj, null);
//        }
//        catch (Exception ex)
//        {
//            throw new Exception(string.Format("Cannot get the value of {0}.{1} See inner exeception", obj, propertyName), ex);
//        }
//    }

//    /// <summary>
//    /// Create the cache item from the controller object
//    /// </summary>
//    /// <param name="parameters">A list of parameters to pass to the controller's method</param>
//    /// <returns>The constructed cache item</returns>
//    private object ConstructCacheItem(params object[] parameters)
//    {
//         return ControllerObject.GetType().InvokeMember(Settings.MethodName, 
//                                                 BindingFlags.Public |
//                                                 BindingFlags.InvokeMethod |
//                                                 BindingFlags.Instance,
//                                                 null, ControllerObject, parameters);				
//    }

//    #endregion
//}

//private class ExecutorList : Dictionary<string, Executor>
//{
//    #region Private Fields

//    #endregion

//    #region Constructors

//    public ExecutorList(CacheProviderSettingsCollection settings)
//    {
//        foreach (CacheProviderSettingsCollection.CacheProviderSettings s in settings)
//        {
//            Add(s.Key, new Executor(s));
//        }
//    }

//    #endregion

//    #region Public Methods

//    /// <summary>
//    /// Add the executor & look for dependancies
//    /// </summary>
//    /// <param name="key"></param>
//    /// <param name="executor"></param>
//    public new void Add(string key, Executor executor)
//    {
//        if (!string.IsNullOrEmpty(executor.Settings.Dependancy) && ContainsKey(executor.Settings.Dependancy))
//        {
//            executor.DependantExecutor = this[executor.Settings.Dependancy];					
//        }

//        base.Add(key, executor);
//    }

//    /// <summary>
//    /// Execute all collection objects
//    /// </summary>
//    public void Execute()
//    {
//        foreach (Executor exec in Values)
//        {
//            exec.Execute();
//        }
//    }

//    #endregion
//}

#endregion